C++11:基于std::unordered

您所在的位置:网站首页 c++ queue 线程安全 C++11:基于std::unordered

C++11:基于std::unordered

2024-07-10 12:22| 来源: 网络整理| 查看: 265

前一篇博客《C++11:基于std::queue和std::mutex构建一个线程安全的队列》中,实现了一个线程安全的队列,本文说说如何实现一个线程安全的map。 在上一篇博客中,实现threadsafe_queue主要是依赖std::mutex信号量来实现线程对threadsafe_queue的独占访问,不论是只读的函数还是写函数对threadsafe_queue都是独占访问,因为对threadsafe_queue中的操作相对较少,而且主要操作push/pop都是写操作,所以这样做是没问题的。 但对于map,除了insert/erase这样的写操作之外还有find这样的读取操作,如果每个线程都是独占访问,无疑是会影响效率的。 所以在实现线程安全的map时,我没有选择使用std::mutex控制所有的操作为独占访问,而是用RWLock来控制map对象的访问,RWLock是我以前自己写的一个类,将线程对资源的访问分为读取操作和写入操作两类,这两类操作是独占的,但允许多个线程读取操作,允许一个线程写访问。也就是说多个线程在读取操作的时候,要写入的线程是阻塞的,直到所读取操作线程执行完读取操作释放读取锁,反之亦然,如果有一个线程在执行写入操作,所有要读取操作的线程就得等着,直到写入操作结束。

关于RWLock的源码及更详细的说明参见我的博客《无锁编程:c++11基于atomic实现共享读写锁(写优先)》

有了RWLock,基于std::unordered_map实现线程安全的map就比较简单了,基本上是把unordered_map的源码抄了一遍,对于unordered_map中的每个函数入口加一个RWLock的读取锁或写入锁。 实现的基本原则很简单: 对于const函数加读取锁,允许共享读取, 对于非const函数,加写入锁,允许独占写入。 下面是完整的源码:

/* * threadsafe_unordered_map.h * * Created on: 2016年7月26日 * Author: guyadong */ #ifndef COMMON_SOURCE_CPP_THREADSAFE_UNORDERED_MAP_H_ #define COMMON_SOURCE_CPP_THREADSAFE_UNORDERED_MAP_H_ #include #include #include #include "RWLock.h" namespace gdface { inline namespace mt{ /* * 基于std::unordered_map实现线程安全map * 禁止复制构造函数 * 禁止复制赋值操作符 * 允许移动构造函数 * 禁止移动赋值操作符 * */ template class threadsafe_unordered_map{ private: std::unordered_map map; // 用于控制读写访问的锁对象 mutable RWLock lock; public: using map_type=std::unordered_map; using key_type=typename map_type::key_type; using mapped_type=typename map_type::mapped_type; using value_type=typename map_type::value_type; using hasher=typename map_type::hasher; using key_equal=typename map_type::key_equal; using allocator_type=typename map_type::allocator_type; using reference=typename map_type::reference; using const_reference=typename map_type::const_reference; using pointer=typename map_type::pointer; using const_pointer=typename map_type::const_pointer; using iterator=typename map_type::iterator; using const_iterator=typename map_type::const_iterator; using local_iterator=typename map_type::local_iterator; using const_local_iterator=typename map_type::const_local_iterator; using size_type=typename map_type::size_type; using difference_type=typename map_type::difference_type; threadsafe_unordered_map()=default; threadsafe_unordered_map(const threadsafe_unordered_map&)=delete; threadsafe_unordered_map(threadsafe_unordered_map&&)=default; threadsafe_unordered_map& operator=(const threadsafe_unordered_map&)=delete; threadsafe_unordered_map& operator=(threadsafe_unordered_map&&)=delete; explicit threadsafe_unordered_map(size_type __n, const hasher& __hf = hasher(), const key_equal& __eql = key_equal(), const allocator_type& __a = allocator_type()):map(__n,__hf,__eql,__a){} template threadsafe_unordered_map(_InputIterator __first, _InputIterator __last, size_type __n = 0, const hasher& __hf = hasher(), const key_equal& __eql = key_equal(), const allocator_type& __a = allocator_type()):map(__first,__last,__n,__hf,__eql,__a){} threadsafe_unordered_map(const map_type&v): map(v){} threadsafe_unordered_map(map_type&&rv):map(std::move(rv)){} explicit threadsafe_unordered_map(const allocator_type& __a):map(__a){} threadsafe_unordered_map(const map_type& __umap, const allocator_type& __a):map(__umap,__a){} threadsafe_unordered_map(map_type&& __umap, const allocator_type& __a):map(std::move(__umap),__a){} threadsafe_unordered_map(initializer_list __l, size_type __n = 0, const hasher& __hf = hasher(), const key_equal& __eql = key_equal(), const allocator_type& __a = allocator_type()):map(__l,__n,__hf,__eql,__a){} threadsafe_unordered_map(size_type __n, const allocator_type& __a) : threadsafe_unordered_map(__n, hasher(), key_equal(), __a){} threadsafe_unordered_map(size_type __n, const hasher& __hf, const allocator_type& __a) : threadsafe_unordered_map(__n, __hf, key_equal(), __a){} template threadsafe_unordered_map(_InputIterator __first, _InputIterator __last, size_type __n, const allocator_type& __a):map(__first,__last,__n,__a){} template threadsafe_unordered_map(_InputIterator __first, _InputIterator __last, size_type __n, const hasher& __hf, const allocator_type& __a) : threadsafe_unordered_map(__first, __last, __n, __hf, key_equal(), __a){} threadsafe_unordered_map(initializer_list __l, size_type __n, const allocator_type& __a) : threadsafe_unordered_map(__l, __n, hasher(), key_equal(), __a){} threadsafe_unordered_map(initializer_list __l, size_type __n, const hasher& __hf, const allocator_type& __a) : threadsafe_unordered_map(__l, __n, __hf, key_equal(), __a){} bool empty() const noexcept{ auto guard=lock.read_guard(); return map.empty(); } size_type size() const noexcept{ auto guard=lock.read_guard(); return map.size(); } size_type max_size() const noexcept{ auto guard=lock.read_guard(); return map.max_size(); } iterator begin() noexcept{ auto guard=lock.write_guard(); return map.begin(); } const_iterator begin() const noexcept{ auto guard=lock.read_guard(); return map.begin(); } const_iterator cbegin() const noexcept{ auto guard=lock.read_guard(); return map.cbegin(); } iterator end() noexcept{ auto guard=lock.write_guard(); return map.end(); } const_iterator end() const noexcept{ auto guard=lock.read_guard(); return map.end(); } const_iterator cend() const noexcept{ auto guard=lock.read_guard(); return map.cend(); } template std::pair emplace(_Args&&... __args){ auto guard=lock.write_guard(); return map.emplace(std::forward(__args)...); } template iterator emplace_hint(const_iterator __pos, _Args&&... __args){ auto guard=lock.write_guard(); return map.emplace_hint(__pos, std::forward(__args)...); } std::pair insert(const value_type& __x){ auto guard=lock.write_guard(); return map.insert(__x); } template std::pair insert(_Pair&& __x){ auto guard=lock.write_guard(); return map.insert(std::forward(__x)); } iterator insert(const_iterator __hint, const value_type& __x) { auto guard=lock.write_guard(); return map.insert(__hint, __x); } template iterator insert(const_iterator __hint, _Pair&& __x){ auto guard=lock.write_guard(); return map.insert(__hint, std::forward(__x)); } template void insert(_InputIterator __first, _InputIterator __last){ auto guard=lock.write_guard(); map.insert(__first, __last); } void insert(initializer_list __l){ auto guard=lock.write_guard(); map.insert(__l); } iterator erase(const_iterator __position){ auto guard=lock.write_guard(); return map.erase(__position); } iterator erase(iterator __position){ auto guard=lock.write_guard(); return map.erase(__position); } size_type erase(const key_type& __x){ auto guard=lock.write_guard(); return map.erase(__x); } iterator erase(const_iterator __first, const_iterator __last){ auto guard=lock.write_guard(); return map.erase(__first, __last); } void clear() noexcept{ auto guard=lock.write_guard(); map.clear(); } void swap(map_type& __x) noexcept( noexcept(map.swap(__x._M_h)) ){ auto guard=lock.write_guard(); map.swap(__x._M_h); } hasher hash_function() const{ auto guard=lock.read_guard(); return map.hash_function(); } key_equal key_eq() const{ auto guard=lock.read_guard(); return map.key_eq(); } iterator find(const key_type& __x){ auto guard=lock.write_guard(); return map.find(__x); } const_iterator find(const key_type& __x) const{ auto guard=lock.read_guard(); return map.find(__x); } size_type count(const key_type& __x) const { auto guard=lock.read_guard(); return map.count(__x); } std::pair equal_range(const key_type& __x){ auto guard=lock.write_guard(); return map.equal_range(__x); } std::pair equal_range(const key_type& __x) const{ auto guard=lock.read_guard(); return map.equal_range(__x); } mapped_type& operator[](const key_type& __k){ auto guard=lock.write_guard(); return map[__k]; } mapped_type& operator[](key_type&& __k){ auto guard=lock.write_guard(); return map[std::move(__k)]; } mapped_type& at(const key_type& __k){ auto guard=lock.write_guard(); return map.at(__k); } const mapped_type& at(const key_type& __k) const{ auto guard=lock.read_guard(); return map.at(__k); } size_type bucket_count() const noexcept{ auto guard=lock.read_guard(); return map.bucket_count(); } size_type max_bucket_count() const noexcept{ auto guard=lock.read_guard(); return map.max_bucket_count(); } size_type bucket_size(size_type __n) const{ auto guard=lock.read_guard(); return map.bucket_size(__n); } size_type bucket(const key_type& __key) const{ auto guard=lock.read_guard(); return map.bucket(__key); } local_iterator begin(size_type __n) { auto guard=lock.write_guard(); return map.begin(__n); } const_local_iterator begin(size_type __n) const { auto guard=lock.read_guard(); return map.begin(__n); } const_local_iterator cbegin(size_type __n) const{ auto guard=lock.read_guard(); return map.cbegin(__n); } local_iterator end(size_type __n) { auto guard=lock.write_guard(); return map.end(__n); } const_local_iterator end(size_type __n) const{ auto guard=lock.read_guard(); return map.end(__n); } const_local_iterator cend(size_type __n) const{ auto guard=lock.read_guard(); return map.cend(__n); } float load_factor() const noexcept{ auto guard=lock.read_guard(); return map.load_factor(); } float max_load_factor() const noexcept{ auto guard=lock.read_guard(); return map.max_load_factor(); } void max_load_factor(float __z){ auto guard=lock.write_guard(); map.max_load_factor(__z); } void rehash(size_type __n){ auto guard=lock.write_guard(); map.rehash(__n); } void reserve(size_type __n){ auto guard=lock.write_guard(); map.reserve(__n); } /* * 新增加函数,bool值返回是否找到 * 返回true时,将value中置为找到的值 * */ bool find(const key_type& __x, mapped_type &value) const{ auto guard=lock.read_guard(); auto itor=map.find(__x); auto found=itor!=map.end(); if(found) value=itor->second; return found; } /* * 新增加函数,返回读取锁的RAII对象 * 在对map进行读取操作时应该先调用此函数 * */ raii read_guard()const noexcept{ return lock.read_guard(); } /* * 新增加函数,返回写入锁的RAII对象 * 在对map进行写入操作时应该先调用此函数 * */ raii write_guard()noexcept{ return lock.write_guard(); } /* * 新增加函数 * 如果指定的key不存在,则增加key->value映射 * 如果指定的key存在返回key映射的值,否则返回value * */ mapped_type insertIfAbsent(const key_type& key,const mapped_type &value){ auto guard=lock.write_guard(); auto itor=map.find(key); if (itor==map.end()){ map.insert(value_type(key, value)); return value; } return itor->second; } /* * 新增加函数 * 如果指定的key存在,则用value替换key映射的值,返回key原来映射的值 * 否则返回nullptr * */ std::shared_ptr replace(const key_type& key,const mapped_type &value){ auto guard=lock.write_guard(); if (map.find(key)!=map.end()){ map.insert(value_type(key, value)); return std::make_shared(value); } return std::shared_ptr(); } /* * 新增加函数 * 如果存在key->value映射,则用newValue替换key映射的值,返回true * 否则返回false * */ bool replace(const key_type& key,const mapped_type &value,const mapped_type &newValue){ auto guard=lock.write_guard(); auto itor=map.find(key); if (itor!=map.end()&&itor->second==value){ map.insert(value_type(key, newValue)); return true; } return false; } template friend bool operator==(const threadsafe_unordered_map&, const threadsafe_unordered_map&); }; template inline bool operator==(const threadsafe_unordered_map& __x, const threadsafe_unordered_map& __y) { auto guardx=__x.lock.read_guard(); auto guardy=__y.lock.read_guard(); return __x.map._M_equal(__y.map); } template inline bool operator!=(const threadsafe_unordered_map& __x, const threadsafe_unordered_map& __y) { auto guardx=__x.lock.read_guard(); auto guardy=__y.lock.read_guard(); return !(__x == __y); } }/* namespace mt */ }/* namespace gdface */ #endif /* COMMON_SOURCE_CPP_THREADSAFE_UNORDERED_MAP_H_ */

说明: 因为RWLock禁止复制构造函数和赋值操作符,所以threadsafe_unordered_map也禁止复制构造函数和赋值操作符。 另外在类中增加几个用于多线程环境的函数(见源码中的中文注释), 当你需要对map加锁时需要用到raii write_guard()noexcept和raii read_guard()const noexcept。关于这两个函数返回的raii类参见我另一篇博客《C++11实现模板化(通用化)RAII机制》 而bool find(const key_type& __x, mapped_type &value) const则用于多线程环境查找__x对应的值。

下面三个新增加函数是参照java中ConcurrentMap接口实现的

mapped_type insertIfAbsent(const key_type& key,const mapped_type &value); std::shared_ptr replace(const key_type& key,const mapped_type &value); bool replace(const key_type& key,const mapped_type &value,const mapped_type &newValue)

完整代码参见码云git仓库:

https://gitee.com/l0km/common_source_cpp/blob/master/threadsafe_unordered_map.h



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3